package com.stm.bi.report;


import com.stm.base.dto.vo.DictVO;
import com.stm.base.provider.DictionaryProvider;
import com.stm.bi.core.util.TypeConversionUtil;
import com.stm.file.api.AttachmentRemoteService;
import com.stm.file.dto.vo.AttachmentVO;
import com.stm.framework.core.context.UserHolder;
import com.stm.framework.core.util.FileResourceLoader;
import com.stm.framework.core.util.SpringContextUtil;
import com.stm.framework.dbcp.JdbcService;
import com.stm.framework.dto.PageQuery;
import com.stm.framework.dto.Response;
import com.stm.framework.excel.ExcelDataExporter;
import com.stm.framework.excel.model.Column;
import com.stm.framework.model.CustomizePage;
import com.stm.bi.constant.DictionaryConstants;
import com.stm.bi.dto.query.ProductMaterialQry;
import com.stm.bi.dto.vo.*;
import com.stm.bi.dto.vo.ReportTitleVO;
import com.stm.bi.enums.ProductStructType;
import com.stm.bi.jdbc.DataSourceEnum;
import com.stm.bi.jdbc.DataSourceSwitch;
import com.stm.saas.api.ProductConfigRemoteService;
import com.stm.saas.api.ProductStyleRemoteService;
import com.stm.saas.api.WarehouseRemoteService;
import com.stm.saas.dto.query.ProductStylePageQry;
import com.stm.saas.dto.query.WarehousePageByOrgIdQry;
import com.stm.saas.dto.vo.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Component
public class ReportQueryExe {

    private final static Logger log = LoggerFactory.getLogger(ReportQueryExe.class);
    private final static String materialList = "/sql/materialList.sql";
    private final String IMAGE_SIZE = "image/quality,q_60";
    @Resource
    private ExcelDataExporter excelDataExporter;

    @Resource
    private DictionaryProvider dictionaryProvider;
    @Resource
    private ProductStyleRemoteService productStyleRemoteService;
    @Resource
    private AttachmentRemoteService attachmentRemoteService;
    @Resource
    private ProductConfigRemoteService productConfigRemoteService;
    @Resource
    private WarehouseRemoteService warehouseRemoteService;
    @Resource
    private JdbcService jdbcService;


/*    public Response export(HttpServletResponse response, ReportQry reportQry) {
        reportQry.setPageNum(1);
        reportQry.setPageSize(PageQuery.MAX_PAGE_SIZE);

        DimensionTableDataVO dimensionTableData = AbsDatatableQueryExe.exeQuery(reportQry).getData();
        if (dimensionTableData == null || (dimensionTableData.getDatas() == null && dimensionTableData.getTreeData() == null)) {
            return Response.failure("暂无可导出数据");
        }
        List<TitleVO> title = dimensionTableData.getTitle();
        List<Map> datas = getExporterDatas(dimensionTableData);
        List<Column> columns = new ArrayList<>();
        for (TitleVO t : title) {
            columns.add(new Column().setTitle(t.getName()).setFieldName(t.getCode()));
        }
        try {
            excelDataExporter.export(response, columns, datas, "维度数据");
        } catch (Exception e) {
            log.error("导出异常==>", e.getMessage(), e);
            return Response.failure();
        }
        return Response.success();
    }

    static public List<Map> getExporterDatas(DimensionTableDataVO dimensionTableData) {
        List<Map> maps = new ArrayList<>();

        if (dimensionTableData != null && dimensionTableData.getDatas() != null) {
            for (Map<String, String> map : dimensionTableData.getDatas()) {
                Map m = new HashMap();
                for (String k : map.keySet()) {
                    m.put(k, map.get(k));
                }
                maps.add(m);
            }
        }

        if (dimensionTableData != null && dimensionTableData.getTreeData() != null && !dimensionTableData.getTreeData().isEmpty()) {
            List<Map> treeMaps = getTreeDataMaps(dimensionTableData.getTreeData());
            maps.addAll(treeMaps);
        }

        return maps;
    }

    static public List<Map> getTreeDataMaps(List<DimensionTableDataItemVO> dimensionTableDataItems) {
        List<Map> maps = new ArrayList<>();

        for (DimensionTableDataItemVO data : dimensionTableDataItems) {
            Map m = new HashMap();
            for (String k : data.getData().keySet()) {
                m.put(k, data.getData().get(k));
            }
            m.put("index", data.getName());

            maps.add(m);

            if (data.getChildren() != null && !data.getChildren().isEmpty()) {
                List<Map> childrenMaps = getTreeDataMaps(data.getChildren());
                maps.addAll(childrenMaps);
            }
        }
        return maps;
    }*/

    /**
     * @param productMaterialQry
     * @description 商品面料-面料应用
     **/
    public Response<MaterialTableDataVO> materialApply(ProductMaterialQry productMaterialQry) {
        MaterialTableDataVO tableData = new MaterialTableDataVO();
        List<Map<String, String>> datas = new ArrayList<>();
        List<ReportTitleVO> titleList = new ArrayList<>();
        ReportTitleVO title = new ReportTitleVO();
        Long orgId = UserHolder.getOrgId();
        // 设置查询参数
        Integer pageNum = productMaterialQry.getPageNum();
        Integer pageSize = productMaterialQry.getPageSize();
        // 设置表头
        title.setName("季节").setCode("season").setChildren(Collections.singletonList(new ReportTitleVO().setName("面料分类").setCode("materialCategoryName")));
        titleList.add(title);
        // 根据字典查询季节
        List<DictVO> dicts = dictionaryProvider.selectList(DictionaryConstants.SEASON_CONFIG);
        List<DictVO> typeDicts = dictionaryProvider.selectList(DictionaryConstants.PRODUCT_STRUCT_TYPE);
        if (dicts.isEmpty() || typeDicts.isEmpty()) {
            return Response.of(tableData);
        }
        DictVO name = typeDicts.stream().filter(e -> e.getValue().equals(productMaterialQry.getProductDimension())).findFirst().orElse(null);
        dicts = dicts.get(0).getChildren().stream().sorted(Comparator.comparing(DictVO::getIdx)).collect(Collectors.toList());
        for (DictVO dict : dicts) {
            titleList.add(new ReportTitleVO().setName(dict.getLabel()).setCode(dict.getValue()).setChildren(setChildMaterialApplyTitle(name == null ? "" : name.getLabel(), dicts.size(), dict.getValue())));
        }
        // 表头：合计
        titleList.add(new ReportTitleVO().setName("合计").setCode("total").setChildren(setChildMaterialApplyTitle(name == null ? "" : name.getLabel(), dicts.size(), "Total")));
        // 表头：详情
        titleList.add(new ReportTitleVO().setName("详情").setCode("detail"));
        tableData.setTitle(titleList);

        // 根据部门查询面料仓
        List<WarehouseQueryVO> collect = selectWarehouseByDeptIdAndOrgId(productMaterialQry.getDeptId(), orgId);
        if (ObjectUtils.isEmpty(collect)) {
            return Response.of(tableData);
        }
        ReportQueryExe _this = SpringContextUtil.getBean(this.getClass());

        // 查所有面料分类
        List<ValueVO> categoryList = _this.materialCategoryList().getData();

        String sql = "select year,season, material_code as materialCode,material_category_code as materialCategoryCode,material_category_name as materialCategoryName,qty  ";
        // 根据款式、品种、品类区分
        String businessCode;
        StringBuilder sqlBuilder = new StringBuilder(sql);
        if (productMaterialQry.getProductDimension().equals(ProductStructType.STYLE.getValue())) {
            sqlBuilder.append(",style_code as styleCode,style_name as styleName from ads_material_style_year where year = ").append(productMaterialQry.getYear());
            businessCode = "styleCode";
        } else if (productMaterialQry.getProductDimension().equals(ProductStructType.ASSORTMENT.getValue())) {
            sqlBuilder.append(",assortment_code as assortmentCode,assortment_name as assortmentName from ads_material_assortment_year where year = ").append(productMaterialQry.getYear());
            businessCode = "assortmentCode";
        } else {
            sqlBuilder.append(",category_code as categoryCode,category_name as categoryName from ads_material_category_year where year = ").append(productMaterialQry.getYear());
            businessCode = "categoryCode";
        }
        // 查全部
        List<Map> allList = jdbcService.selectList(sqlBuilder + " and org_id = ? ", new Object[]{orgId});
        if (ObjectUtils.isEmpty(allList)) {
            return Response.of(tableData);
        }
        // 根据分类进行筛选
        List<String> materialCategoryCodes = productMaterialQry.getMaterialCategoryCodes();
        if (ObjectUtils.isNotEmpty(materialCategoryCodes)) {
            sqlBuilder.append(" and material_category_code in (");
            for (String materialCategoryCode : productMaterialQry.getMaterialCategoryCodes()) {
                sqlBuilder.append("'").append(materialCategoryCode).append("',");
            }
            sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
            sqlBuilder.append(") ");
        }
        sqlBuilder.append(" and org_id = ?");
        List<Map> selectList = jdbcService.selectList(sqlBuilder.toString(), new Object[]{orgId});
        // 根据数量进行排序并分页
        if (ObjectUtils.isEmpty(selectList)) {
            if (ObjectUtils.isNotEmpty(materialCategoryCodes)) {
                List<String> codes = productMaterialQry.getMaterialCategoryCodes();
                List<DictVO> finalDicts = dicts;
                for (String code : codes) {
                    categoryList.stream().filter(e -> code.equals(e.getValue())).findFirst().ifPresent(e -> {
                        Map<String, String> data = new HashMap<>();
                        for (DictVO dict : finalDicts) {
                            String season = dict.getValue();
                            data.put("num" + season, "0");
                            data.put("numProportion" + season, "0");
                            data.put("sale" + season, "0");
                            data.put("saleProportion" + season, "0");

                        }
                        data.put("materialCategoryCode", e.getValue().toString());
                        data.put("materialCategoryName", e.getLabel());
                        datas.add(data);
                    });
                }
                tableData.setCurrent(Long.valueOf(pageNum)).setSize(Long.valueOf(pageSize)).setTotal((long) codes.size());
                tableData.setDatas(datas);
            }
            return Response.of(tableData);
        }
        // 添加列表里没有但是分类下拉列表里有的分类
        List<Map> addMap = new ArrayList<>();
        List<Map> finalSelectList = selectList;
        categoryList.removeIf(e -> finalSelectList.stream().anyMatch(select -> select.get("materialCategoryCode").equals(e.getValue())));
        if (ObjectUtils.isNotEmpty(materialCategoryCodes)) {
            materialCategoryCodes.removeIf(e -> finalSelectList.stream().anyMatch(select -> select.get("materialCategoryCode").equals(e)));
            if (ObjectUtils.isNotEmpty(materialCategoryCodes)){
                for (String materialCategoryCode : materialCategoryCodes) {
                    categoryList.stream().filter(e -> materialCategoryCode.equals(e.getValue())).findFirst().ifPresent(vo -> {
                        Map map = new HashMap<>();
                        map.put("materialCategoryCode", vo.getValue());
                        map.put("materialCategoryName", vo.getLabel());
                        addMap.add(map);
                    });
                }
            }
        }else {
            for (ValueVO vo : categoryList) {
                Map map = new HashMap<>();
                map.put("materialCategoryCode", vo.getValue());
                map.put("materialCategoryName", vo.getLabel());
//            map.put(businessCode,"0");
                addMap.add(map);
            }
        }
        selectList.addAll(addMap);

        // 总款式
        double totalNum = allList.stream().map(map -> (String) map.get(businessCode)).filter(Objects::nonNull).distinct().count();
        // 总销量
        double totalQty = allList.stream().map(map -> (Integer) map.get("qty")).filter(Objects::nonNull).mapToDouble(Integer::doubleValue).sum();
        // 根据面料分类和季节得到数量
        Map<String, Integer> seasonQtyMap = allList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("season"),
                        Collectors.collectingAndThen(
                                Collectors.mapping(
                                        map -> (String) map.get(businessCode),
                                        Collectors.toSet() // 使用 Set 去重
                                ),
                                Set::size // 统计去重后的数量
                        )
                ));
        // 根据季节获取销量
        Map<String, Integer> seasonTotalMap = allList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("season"),
                        Collectors.summingInt(map -> (Integer) map.get("qty"))
                ));
        // 根据面料分类编码获取销量用来排序
        Map<String, Long> categoryQtyMap = selectList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("materialCategoryCode"),
                        Collectors.summingLong(map ->
                                Optional.ofNullable((String) map.get(businessCode))
                                        .map(s -> 1L)
                                        .orElse(0L)
                        )
                ));


        List<Map.Entry<String, Long>> sortedCategoryQtyList = categoryQtyMap.entrySet().stream()
                .sorted((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()))
                .collect(Collectors.toList());
        // 添加列表里没有但是分类下拉列表里有的分类
        /*List<Map> finalSelectList = selectList;
        categoryList.removeIf(e -> finalSelectList.stream().anyMatch(select -> select.get("materialCategoryCode").equals(e.getValue())));
        List<Map.Entry<String, Long>> addMap = categoryList.stream()
                .map(vo -> new AbstractMap.SimpleEntry<>((String) vo.getValue(), 0L))
                .collect(Collectors.toList());
        sortedCategoryQtyList.addAll(addMap);*/

        // 分页操作
        if (pageNum == 1) {
            sortedCategoryQtyList = sortedCategoryQtyList.subList(0, Math.min(sortedCategoryQtyList.size(), pageSize));
        } else {
            sortedCategoryQtyList = sortedCategoryQtyList.subList(
                    Math.min(sortedCategoryQtyList.size(), (pageNum - 1) * pageSize),
                    Math.min(sortedCategoryQtyList.size(), pageNum * pageSize)
            );
        }
        List<Map.Entry<String, Long>> finalSortedCategoryQtyList = sortedCategoryQtyList;
        selectList = selectList.stream()
                .filter(map -> finalSortedCategoryQtyList.stream().anyMatch(entry -> entry.getKey().equals(map.get("materialCategoryCode"))))
                .collect(Collectors.toList());

        // 使用 LinkedHashMap 来保持插入顺序
        Map<String, Map<String, List<Map>>> materialMaps = new LinkedHashMap<>();
        for (Map.Entry<String, Long> entry : finalSortedCategoryQtyList) {
            String materialCode = entry.getKey();
            List<Map> filteredList = selectList.stream()
                    .filter(map -> materialCode.equals(map.get("materialCategoryCode")))
                    .collect(Collectors.toList());

            // 根据季节分组
            Map<String, List<Map>> seasonMap = filteredList.stream()
                    .filter(map -> map.get("season") != null)
                    .collect(Collectors.groupingBy(
                            map -> (String) map.get("season")
                    ));

            materialMaps.put(materialCode, seasonMap);
        }
        // 最终计算逻辑
        for (String categoryCode : materialMaps.keySet()) {
            // 面料分类
            Map<String, String> data = new HashMap<>();
            data.put("materialCategoryCode", categoryCode);
            Map<String, List<Map>> seasonListMap = materialMaps.get(categoryCode);
            int num = 0;
            BigDecimal proportion = BigDecimal.ZERO;
            int saleNum = 0;
            BigDecimal saleProportion = BigDecimal.ZERO;
            for (DictVO dict : dicts) {
                String season = dict.getValue();
                // 季节分类
                List<Map> maps = seasonListMap.get(season);
                if (maps == null || maps.isEmpty()) {
                    data.put("num" + season, "0");
                    data.put("numProportion" + season, "0");
                    data.put("sale" + season, "0");
                    data.put("saleProportion" + season, "0");
                    selectList.stream().filter(e -> e.get("materialCategoryCode").equals(categoryCode)).findFirst().ifPresent(e -> data.put("materialCategoryName", (String) e.get("materialCategoryName")));
                    continue;
                }
                data.put("materialCategoryName", (String) maps.get(0).get("materialCategoryName"));
                // 数量
                long styleNum = maps.stream().map(e -> e.get(businessCode)).filter(Objects::nonNull).distinct().count();

                // 数量
                num += (int) styleNum;
                data.put("num" + season, String.valueOf(styleNum));
                // 数量占比
                double totalStyle = seasonQtyMap.get(season).doubleValue();
                if (totalStyle != 0.0) {
                    BigDecimal result = BigDecimal.valueOf(100 * styleNum / totalStyle).setScale(2, RoundingMode.HALF_UP);
                    proportion = proportion.add(result);
                    data.put("numProportion" + season, result.toString() + "%");
                } else {
                    data.put("numProportion" + season, "0");
                }
                // 销量
                double sum = maps.stream().map(map -> (Integer) map.get("qty")).mapToDouble(Integer::doubleValue).sum();
                data.put("sale" + season, String.valueOf(sum));
                saleNum += (int) sum;
                // 销量占比
                double totalSale = seasonTotalMap.get(season).doubleValue();
                if (totalSale != 0.0) {
                    BigDecimal saleResult = BigDecimal.valueOf(100 * sum / totalSale).setScale(2, RoundingMode.HALF_UP);
                    data.put("saleProportion" + season, saleResult.toString() + "%");
                    saleProportion = saleProportion.add(saleResult);
                } else {
                    data.put("saleProportion" + season, "0%");
                }
            }
            // 合计
            data.put("numTotal", String.valueOf(num));
            // 总数量占比
            if (totalNum != 0) {
                data.put("numProportionTotal", BigDecimal.valueOf(100 * (num / totalNum)).setScale(2, RoundingMode.HALF_UP) + "%");
            } else {
                data.put("numProportionTotal", "0");
            }
            // 总销量占比
            data.put("saleTotal", String.valueOf(saleNum));
            if (totalQty != 0) {

                data.put("saleProportionTotal", BigDecimal.valueOf(100 * (saleNum / totalQty)).setScale(2, RoundingMode.HALF_UP) + "%");
            } else {
                data.put("saleProportionTotal", "0%");
            }
            datas.add(data);
        }

        // 计算并统计表格数据
        tableData.setCurrent(Long.valueOf(pageNum)).setSize(Long.valueOf(pageSize)).setTotal((long) categoryQtyMap.size());
        tableData.setDatas(datas);
        return Response.of(tableData);
    }

    /**
     * 商品面料分类下拉列表
     **/
    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<List<ValueVO>> materialCategoryList() {
        List<ValueVO> values = new ArrayList<>();
        Long orgId = UserHolder.getOrgId();
        String selectSql = FileResourceLoader.getResourceAsString("/sql/materialList.sql");
        List<Map> list = jdbcService.selectList(selectSql, new Object[]{orgId, orgId});
        if (list != null && !list.isEmpty()) {
            for (Map map : list) {
                ValueVO value = new ValueVO();
                value.setLabel((String) map.get("categoryName")).setValue(map.get("categoryCode"));
                values.add(value);
            }
        }
        return Response.of(values);
    }

    /**
     * 商品面料下拉列表
     **/
    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<List<ValueVO>> materialList(ProductMaterialQry productMaterialQry) {
        List<ValueVO> values = new ArrayList<>();
        Long orgId = UserHolder.getOrgId();
        String sql = "SELECT\n" +
                "    m.`code`\n" +
                "FROM\n" +
                "    ods_material m\n" +
                "WHERE\n" +
                "    m.org_id = ?\n";
        if (productMaterialQry.getMaterialCategoryCodes() != null && !productMaterialQry.getMaterialCategoryCodes().isEmpty()) {
            sql += " AND m.category_code IN(";
            List<String> materialCategoryCodes = productMaterialQry.getMaterialCategoryCodes();
            StringBuilder sb = new StringBuilder();
            for (String materialCategoryCode : materialCategoryCodes) {
                sb.append("'").append(materialCategoryCode).append("'").append(",");
            }
            sb.delete(sb.length() - 1, sb.length());
            sb.append(")");
            sql += sb.toString();
            sql += " GROUP BY m.`code`";
        }
        List<Map> list = jdbcService.selectList(sql, new Object[]{orgId});
        for (Map map : list) {
            values.add(new ValueVO().setLabel((String) map.get("code")).setValue(map.get("code")));
        }
        return Response.of(values);
    }

    /**
     * 商品款式明细
     **/
    public Response<MaterialTableDataVO> styleDetail(ProductMaterialQry productMaterialQry) {
        MaterialTableDataVO tableData = new MaterialTableDataVO();
        List<Map<String, String>> datas = new ArrayList<>();
        List<ReportTitleVO> titleList = new ArrayList<>();
        ReportTitleVO title = new ReportTitleVO();
        Long orgId = UserHolder.getOrgId();
        // 设置查询参数
        Integer pageNum = productMaterialQry.getPageNum();
        Integer pageSize = productMaterialQry.getPageSize();
        Map<String, List<AttachmentVO>> imageMap = new HashMap<>();
        List<ReportTitleVO> titles = new ArrayList<>();
        // 根据字典查询季节
        List<DictVO> dicts = dictionaryProvider.selectList(DictionaryConstants.SEASON_CONFIG);
        List<DictVO> typeDicts = dictionaryProvider.selectList(DictionaryConstants.PRODUCT_STRUCT_TYPE);
        if (dicts.isEmpty() || typeDicts.isEmpty()) {
            return Response.of(tableData);
        }
        dicts = dicts.get(0).getChildren().stream().sorted(Comparator.comparing(DictVO::getIdx)).collect(Collectors.toList());
        DictVO name = typeDicts.stream().filter(e -> e.getValue().equals(productMaterialQry.getProductDimension())).findFirst().orElse(null);
        // 设置表头
        List<DictVO> styleDicts = dicts.subList(0, 4);
        title.setName("商品").setCode("product").setChildren(titles);
        titleList.add(title);
        for (DictVO dict : styleDicts) {
            titleList.add(new ReportTitleVO().setName(dict.getLabel()).setCode(dict.getValue()).setChildren(setChildMaterialApplyTitle(null, dicts.size(), dict.getValue())));
        }
        // 表头：合计
        titleList.add(new ReportTitleVO().setName("合计").setCode("total").setChildren(setChildMaterialApplyTitle(null, dicts.size(), "Total")));

        // 根据部门查询面料仓
        List<WarehouseQueryVO> collect = selectWarehouseByDeptIdAndOrgId(productMaterialQry.getDeptId(), orgId);
        if (ObjectUtils.isEmpty(collect)) {
            return Response.of(tableData);
        }

        String sql = "select year,month, material_code as materialCode,material_category_code as materialCategoryCode,material_category_name as materialCategoryName,qty  ";
        // 根据款式、品种、品类区分
        String businessCode;
        StringBuilder sqlBuilder = new StringBuilder(sql);
        if (productMaterialQry.getProductDimension().equals(ProductStructType.STYLE.getValue())) {
            sqlBuilder.append(",style_code as styleCode,style_name as styleName from ads_material_product_style_month where year = ").append(productMaterialQry.getYear());
            businessCode = "styleCode";
            // 款式明细
            titles.add(new ReportTitleVO().setName("款式名称").setCode("styleName"));
            titles.add(new ReportTitleVO().setName("款式编码").setCode("styleCode"));
            titles.add(new ReportTitleVO().setName("图片").setCode("img"));
        } else if (productMaterialQry.getProductDimension().equals(ProductStructType.ASSORTMENT.getValue())) {
            sqlBuilder.append(",assortment_code as assortmentCode,assortment_name as assortmentName from ads_material_product_assortment_month where year = ").append(productMaterialQry.getYear());
            businessCode = "assortmentCode";
            // 品种
            titles.add(new ReportTitleVO().setName("品种名称").setCode("styleName"));
        } else {
            sqlBuilder.append(",category_code as categoryCode,category_name as categoryName from ads_material_product_category_month where year = ").append(productMaterialQry.getYear());
            businessCode = "categoryCode";
            // 品种
            titles.add(new ReportTitleVO().setName("品类名称").setCode("styleName"));
        }
        tableData.setTitle(titleList);
        // 查全部
        List<Map> allList = jdbcService.selectList(sqlBuilder + " and org_id = ? ", new Object[]{orgId});
        if (ObjectUtils.isEmpty(allList)) {
            return Response.of(tableData);
        }

        // 根据分类进行筛选
        if (productMaterialQry.getMaterialCategoryCodes() != null && !productMaterialQry.getMaterialCategoryCodes().isEmpty()) {
            sqlBuilder.append(" and material_category_code in (");
            for (String materialCategoryCode : productMaterialQry.getMaterialCategoryCodes()) {
                sqlBuilder.append("'").append(materialCategoryCode).append("',");
            }
            sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
            sqlBuilder.append(") ");
        }
        // 根据面料进行筛选
        if (ObjectUtils.isNotEmpty(productMaterialQry.getMaterialCode())) {
            sqlBuilder.append(" and material_code = '").append(productMaterialQry.getMaterialCode()).append("'");
        }
        sqlBuilder.append(" and org_id = ?");
        List<Map> selectList = jdbcService.selectList(sqlBuilder.toString(), new Object[]{orgId});
        // 根据数量进行排序并分页
        if (ObjectUtils.isEmpty(selectList)) {
            return Response.of(tableData);
        }
        // 查询商品季节配置
        Response<ProductConfigVO> productConfigResponse = productConfigRemoteService.selectByOrgId(orgId);
        if (!productConfigResponse.isSuccess() || productConfigResponse.getData() == null) {
            return Response.of(tableData);
        }
        List<ProductConfigSeasonVO> productConfigSeason = productConfigResponse.get().getItems();

        // 构建seasonCode到value的映射
        Map<String, String> seasonCodeToValueMap = productConfigSeason.stream()
                .collect(Collectors.toMap(ProductConfigSeasonVO::getSeasonCode, ProductConfigSeasonVO::getSeasonCode));

        // 构建value到label的映射
        Map<String, String> valueToLabelMap = styleDicts.stream()
                .collect(Collectors.toMap(DictVO::getValue, DictVO::getValue));

        // 构建seasonCode到月份范围的映射
        Map<String, int[]> seasonCodeToMonthRangeMap = new HashMap<>();
        for (ProductConfigSeasonVO config : productConfigSeason) {
            int startMonth = Integer.parseInt(config.getStartDate());
            int endMonth = Integer.parseInt(config.getEndDate());
            seasonCodeToMonthRangeMap.put(config.getSeasonCode(), new int[]{startMonth, endMonth});
        }

        // 遍历selectList，匹配季节并插入season字段
        for (Map data : selectList) {
            int month = (int) data.get("month");
            String matchedSeasonLabel = null;

            for (Map.Entry<String, int[]> entry : seasonCodeToMonthRangeMap.entrySet()) {
                int startMonth = entry.getValue()[0];
                int endMonth = entry.getValue()[1];
                if (month >= startMonth && month <= endMonth) {
                    String seasonCode = entry.getKey();
                    String seasonValue = seasonCodeToValueMap.get(seasonCode);
                    matchedSeasonLabel = valueToLabelMap.get(seasonValue);
                    break;
                }
            }
            if (matchedSeasonLabel != null) {
                data.put("season", matchedSeasonLabel);
            }
        }
        // 遍历selectList，匹配季节并插入season字段
        for (Map data : allList) {
            int month = (int) data.get("month");
            String matchedSeasonLabel = null;

            for (Map.Entry<String, int[]> entry : seasonCodeToMonthRangeMap.entrySet()) {
                int startMonth = entry.getValue()[0];
                int endMonth = entry.getValue()[1];
                if (month >= startMonth && month <= endMonth) {
                    String seasonCode = entry.getKey();
                    String seasonValue = seasonCodeToValueMap.get(seasonCode);
                    matchedSeasonLabel = valueToLabelMap.get(seasonValue);
                    break;
                }
            }
            if (matchedSeasonLabel != null) {
                data.put("season", matchedSeasonLabel);
            }
        }

        // 总销量
        double totalQty = allList.stream().map(map -> (Integer) map.get("qty")).filter(Objects::nonNull).mapToDouble(Integer::doubleValue).sum();
        // 根据季节获取销量
        Map<String, Integer> seasonTotalMap = allList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("season"),
                        Collectors.summingInt(map -> (Integer) map.get("qty"))
                ));
        // 根据业务编码获取销量用来排序
        Map<String, Integer> categoryQtyMap = selectList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get(businessCode),
                        Collectors.summingInt(map -> (Integer) map.get("qty"))
                ));

        List<Map.Entry<String, Integer>> sortedCategoryQtyList = categoryQtyMap.entrySet().stream()
                .sorted((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()))
                .collect(Collectors.toList());

        // 分页操作
        if (pageNum == 1) {
            sortedCategoryQtyList = sortedCategoryQtyList.subList(0, Math.min(sortedCategoryQtyList.size(), pageSize));
        } else {
            sortedCategoryQtyList = sortedCategoryQtyList.subList(
                    Math.min(sortedCategoryQtyList.size(), (pageNum - 1) * pageSize),
                    Math.min(sortedCategoryQtyList.size(), pageNum * pageSize)
            );
        }
        List<Map.Entry<String, Integer>> finalSortedCategoryQtyList = sortedCategoryQtyList;
        selectList = selectList.stream()
                .filter(map -> finalSortedCategoryQtyList.stream().anyMatch(entry -> entry.getKey().equals(map.get(businessCode))))
                .collect(Collectors.toList());

        // 使用 LinkedHashMap 来保持插入顺序
        Map<String, Map<String, List<Map>>> materialMaps = new LinkedHashMap<>();
        for (Map.Entry<String, Integer> entry : finalSortedCategoryQtyList) {
            String materialCode = entry.getKey();
            List<Map> filteredList = selectList.stream()
                    .filter(map -> materialCode.equals(map.get(businessCode)))
                    .collect(Collectors.toList());

            // 根据季节分组
            Map<String, List<Map>> seasonMap = filteredList.stream()
                    .collect(Collectors.groupingBy(
                            map -> (String) map.get("season")
                    ));

            materialMaps.put(materialCode, seasonMap);
        }
        // 款式需要查询图片
        List<ProductStyleQueryVO> productStyleList;
        if (productMaterialQry.getProductDimension().equals(ProductStructType.STYLE.getValue())) {
            List<String> styleCodes = new ArrayList<>(materialMaps.keySet());
            ProductStylePageQry productStylePageQry = new ProductStylePageQry();
            productStylePageQry.setOrgId(orgId).setIsProduct(1).setCodes(styleCodes);
            Response<List<ProductStyleQueryVO>> productStyleResponse = productStyleRemoteService.selectList(productStylePageQry);
            if (!productStyleResponse.isSuccess() && productStyleResponse.getData().isEmpty()) {
                return Response.of(tableData);
            }
            productStyleList = productStyleResponse.get();

            // 查询图片地址
            List<String> imageIds = productStyleList.stream()
                    .map(ProductStyleQueryVO::getImageBusinessId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            if (!imageIds.isEmpty()) {
                Response<Map<String, List<AttachmentVO>>> imageResp = attachmentRemoteService.selectMapByBusinessIds(imageIds, IMAGE_SIZE);
                if (!imageResp.isSuccess()) {
                    return Response.of(tableData);
                }
                imageMap = imageResp.getData();
            } else {
                imageMap = new HashMap<>();
            }
        } else {
            productStyleList = new ArrayList<>();
        }

        // 最终计算逻辑
        for (String code : materialMaps.keySet()) {
            // 面料分类
            Map<String, String> data = new HashMap<>();
            Map<String, List<Map>> seasonListMap = materialMaps.get(code);
            int saleNum = 0;
            BigDecimal saleProportion = BigDecimal.ZERO;
            if (productMaterialQry.getProductDimension().equals(ProductStructType.STYLE.getValue())) {
                // 商品信息和图片设置
                Map<String, List<AttachmentVO>> finalImageMap = imageMap;
                productStyleList.stream().filter(e -> e.getCode().equals(code)).findFirst().ifPresent(e -> {
                    data.put("styleName", e.getName());
                    data.put("styleCode", e.getCode());
                    // 图片地址
                    if (!finalImageMap.isEmpty() && e.getImageBusinessId() != null && ObjectUtils.isNotEmpty(finalImageMap.get(e.getImageBusinessId()))) {
                        data.put("img", finalImageMap.get(e.getImageBusinessId()).get(0).getFullPath());
                    }
                });
            } else if (productMaterialQry.getProductDimension().equals(ProductStructType.ASSORTMENT.getValue())) {
                selectList.stream().filter(e -> e.get(businessCode).equals(code)).findFirst().ifPresent(e -> data.put("styleName", (String) e.get("assortmentName")));
            } else {
                selectList.stream().filter(e -> e.get(businessCode).equals(code)).findFirst().ifPresent(e -> data.put("styleName", (String) e.get("categoryName")));
            }
            for (DictVO dict : dicts) {
                String season = dict.getValue();
                // 季节分类
                List<Map> maps = seasonListMap.get(season);
                if (maps == null || maps.isEmpty()) {
                    data.put("sale" + season, "0");
                    data.put("saleProportion" + season, "0%");
                    continue;
                }

                // 销量
                double sum = maps.stream().map(map -> (Integer) map.get("qty")).mapToDouble(Integer::doubleValue).sum();
                data.put("sale" + season, String.valueOf(sum));
                saleNum += (int) sum;
                // 销量占比
                double totalSale = seasonTotalMap.get(season).doubleValue();
                if (totalSale != 0.0) {
                    BigDecimal saleResult = BigDecimal.valueOf(100 * (sum / totalSale)).setScale(2, RoundingMode.HALF_UP);
                    data.put("saleProportion" + season, saleResult.toString() + "%");
                    saleProportion = saleProportion.add(saleResult);
                } else {
                    data.put("saleProportion" + season, "0%");
                }
            }
            // 合计
            // 总销量占比
            data.put("saleTotal", String.valueOf(saleNum));
            if (totalQty != 0) {
                data.put("saleProportionTotal", BigDecimal.valueOf(100 * (saleNum / totalQty)).setScale(2, RoundingMode.HALF_UP) + "%");
            } else {
                data.put("saleProportionTotal", "0%");
            }
            datas.add(data);
        }

        // 计算并统计表格数据
        tableData.setCurrent(Long.valueOf(pageNum)).setSize(Long.valueOf(pageSize)).setTotal((long) categoryQtyMap.size());
        tableData.setDatas(datas);
        return Response.of(tableData);
    }


    /**
     * 商品面料明细
     **/
    public Response<MaterialTableDataVO> materialDetail(ProductMaterialQry productMaterialQry) {
        MaterialTableDataVO tableData = new MaterialTableDataVO();
        List<Map<String, String>> datas = new ArrayList<>();
        List<ReportTitleVO> titleList = new ArrayList<>();
        ReportTitleVO title = new ReportTitleVO();
        Long orgId = UserHolder.getOrgId();
        // 设置查询参数
        Integer pageNum = productMaterialQry.getPageNum();
        Integer pageSize = productMaterialQry.getPageSize();
        // 根据字典查询季节
        List<DictVO> dicts = dictionaryProvider.selectList(DictionaryConstants.SEASON_CONFIG);
        List<DictVO> typeDicts = dictionaryProvider.selectList(DictionaryConstants.PRODUCT_STRUCT_TYPE);
        if (dicts.isEmpty() || typeDicts.isEmpty()) {
            return Response.of(tableData);
        }
        dicts = dicts.get(0).getChildren().stream().sorted(Comparator.comparing(DictVO::getIdx)).collect(Collectors.toList());
        // 设置表头
        DictVO name = typeDicts.stream().filter(e -> e.getValue().equals(productMaterialQry.getProductDimension())).findFirst().orElse(null);
        title.setName("季节").setCode("season").setChildren(Collections.singletonList(new ReportTitleVO().setName("面料编码").setCode("materialCode")));
        titleList.add(title);
        for (DictVO dict : dicts) {
            titleList.add(new ReportTitleVO().setName(dict.getLabel()).setCode(dict.getValue()).setChildren(setChildMaterialApplyTitle(name == null ? "" : name.getLabel(), dicts.size(), dict.getValue())));
        }
        // 表头：合计
        titleList.add(new ReportTitleVO().setName("合计").setCode("total").setChildren(setChildMaterialApplyTitle(name == null ? "" : name.getLabel(), dicts.size(), "Total")));
        tableData.setTitle(titleList);

        // 根据部门查询面料仓
        List<WarehouseQueryVO> collect = selectWarehouseByDeptIdAndOrgId(productMaterialQry.getDeptId(), orgId);
        if (ObjectUtils.isEmpty(collect)) {
            return Response.of(tableData);
        }

        // 查分类下所有面料编码
        ReportQueryExe bean = SpringContextUtil.getBean(this.getClass());
        List<ValueVO> values = bean.materialList(productMaterialQry).get();

        String sql = "select year,season, material_code as materialCode,material_category_code as materialCategoryCode,material_category_name as materialCategoryName,qty  ";
        // 根据款式、品种、品类区分
        String businessCode;
        StringBuilder sqlBuilder = new StringBuilder(sql);
        if (productMaterialQry.getProductDimension().equals(ProductStructType.STYLE.getValue())) {
            sqlBuilder.append(",style_code as styleCode,style_name as styleName from ads_material_style_year where year = ").append(productMaterialQry.getYear());
            businessCode = "styleCode";
        } else if (productMaterialQry.getProductDimension().equals(ProductStructType.ASSORTMENT.getValue())) {
            sqlBuilder.append(",assortment_code as assortmentCode,assortment_name as assortmentName from ads_material_assortment_year where year = ").append(productMaterialQry.getYear());
            businessCode = "assortmentCode";
        } else {
            sqlBuilder.append(",category_code as categoryCode,category_name as categoryName from ads_material_category_year where year = ").append(productMaterialQry.getYear());
            businessCode = "categoryCode";
        }
        // 查全部
        List<Map> allList = jdbcService.selectList(sqlBuilder + " and org_id = ? ", new Object[]{orgId});
        if (ObjectUtils.isEmpty(allList)) {
            return Response.of(tableData);
        }
        // 根据分类进行筛选
        if (productMaterialQry.getMaterialCategoryCodes() != null && !productMaterialQry.getMaterialCategoryCodes().isEmpty()) {
            sqlBuilder.append(" and material_category_code in (");
            for (String materialCategoryCode : productMaterialQry.getMaterialCategoryCodes()) {
                sqlBuilder.append("'").append(materialCategoryCode).append("',");
            }
            sqlBuilder.deleteCharAt(sqlBuilder.length() - 1);
            sqlBuilder.append(") ");
        }
        String qryMaterialCode = productMaterialQry.getMaterialCode();
        if (ObjectUtils.isNotEmpty(qryMaterialCode)) {
            sqlBuilder.append(" and material_code = '").append(qryMaterialCode).append("'");
        }
        sqlBuilder.append(" and org_id = ?");
        List<Map> selectList = jdbcService.selectList(sqlBuilder.toString(), new Object[]{orgId});
        // 根据数量进行排序并分页
        if (ObjectUtils.isEmpty(selectList)) {
            List<DictVO> finalDicts = dicts;
            if (ObjectUtils.isNotEmpty(qryMaterialCode)) {
                values.stream().filter(e -> qryMaterialCode.equals(e.getValue())).findFirst().ifPresent(e -> {
                    Map<String, String> data = new HashMap<>();
                    for (DictVO dict : finalDicts) {
                        String season = dict.getValue();
                        data.put("num" + season, "0");
                        data.put("numProportion" + season, "0");
                        data.put("sale" + season, "0");
                        data.put("saleProportion" + season, "0");

                    }
                    data.put("materialCode", e.getValue().toString());
                    data.put("materialName", e.getLabel());
                    datas.add(data);
                });
            } else {
                for (ValueVO value : values) {
                    Map<String, String> data = new HashMap<>();
                    for (DictVO dict : finalDicts) {
                        String season = dict.getValue();
                        data.put("num" + season, "0");
                        data.put("numProportion" + season, "0");
                        data.put("sale" + season, "0");
                        data.put("saleProportion" + season, "0");

                    }
                    data.put("materialCode", value.getValue().toString());
                    data.put("materialName", value.getLabel());
                    datas.add(data);
                }
            }
            tableData.setCurrent(Long.valueOf(pageNum)).setSize(Long.valueOf(pageSize)).setTotal((long) values.size());
            tableData.setDatas(datas);
            return Response.of(tableData);
        }
        // 添加列表里没有但是分类下拉列表里有的分类
        List<Map> addMap = new ArrayList<>();
        List<Map> finalSelectList = selectList;
        values.removeIf(e -> finalSelectList.stream().anyMatch(select -> select.get("materialCode").equals(e.getValue())));
        for (ValueVO value : values) {
            Map map = new HashMap<>();
            map.put("materialCode", value.getValue());
            map.put("materialName", value.getLabel());
//            map.put(businessCode,"0");
            addMap.add(map);
        }
        selectList.addAll(addMap);
        // 总款式
        double totalNum = allList.stream().map(map -> (String) map.get(businessCode)).filter(Objects::nonNull).count();
        // 总销量
        double totalQty = allList.stream().map(map -> (Integer) map.get("qty")).filter(Objects::nonNull).mapToDouble(Integer::doubleValue).sum();
        // 根据季节得到数量
        Map<String, Integer> seasonQtyMap = allList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("season"),
                        Collectors.collectingAndThen(
                                Collectors.mapping(
                                        map -> (String) map.get(businessCode),
                                        Collectors.toSet() // 使用 Set 去重
                                ),
                                Set::size // 统计去重后的数量
                        )
                ));
        // 根据季节获取销量
        Map<String, Integer> seasonTotalMap = allList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("season"),
                        Collectors.summingInt(map -> (Integer) map.get("qty"))
                ));
        // 根据面料编码获取销量用来排序
        Map<String, Long> categoryQtyMap = selectList.stream()
                .collect(Collectors.groupingBy(
                        map -> (String) map.get("materialCode"),
                        Collectors.summingLong(map ->
                                Optional.ofNullable((String) map.get(businessCode))
                                        .map(s -> 1L)
                                        .orElse(0L)
                        )
                ));

        List<Map.Entry<String, Long>> sortedCategoryQtyList = categoryQtyMap.entrySet().stream()
                .sorted((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()))
                .collect(Collectors.toList());

        // 分页操作
        if (pageNum == 1) {
            sortedCategoryQtyList = sortedCategoryQtyList.subList(0, Math.min(sortedCategoryQtyList.size(), pageSize));
        } else {
            sortedCategoryQtyList = sortedCategoryQtyList.subList(
                    Math.min(sortedCategoryQtyList.size(), (pageNum - 1) * pageSize),
                    Math.min(sortedCategoryQtyList.size(), pageNum * pageSize)
            );
        }
        List<Map.Entry<String, Long>> finalSortedCategoryQtyList = sortedCategoryQtyList;
        selectList = selectList.stream()
                .filter(map -> finalSortedCategoryQtyList.stream().anyMatch(entry -> entry.getKey().equals(map.get("materialCode"))))
                .collect(Collectors.toList());

        // 使用 LinkedHashMap 来保持插入顺序
        Map<String, Map<String, List<Map>>> materialMaps = new LinkedHashMap<>();
        for (Map.Entry<String, Long> entry : finalSortedCategoryQtyList) {
            String materialCode = entry.getKey();
            List<Map> filteredList = selectList.stream()
                    .filter(map -> materialCode.equals(map.get("materialCode")))
                    .collect(Collectors.toList());

            // 根据季节分组
            Map<String, List<Map>> seasonMap = filteredList.stream()
                    .filter(map -> map.get("season") != null)
                    .collect(Collectors.groupingBy(
                            map -> (String) map.get("season")
                    ));

            materialMaps.put(materialCode, seasonMap);
        }

        // 最终计算逻辑
        for (String materialCode : materialMaps.keySet()) {
            // 面料分类
            Map<String, String> data = new HashMap<>();
            data.put("materialCode", materialCode);
            Map<String, List<Map>> seasonListMap = materialMaps.get(materialCode);
            int num = 0;
            BigDecimal proportion = BigDecimal.ZERO;
            int saleNum = 0;
            BigDecimal saleProportion = BigDecimal.ZERO;
            for (DictVO dict : dicts) {
                String season = dict.getValue();
                // 季节分类
                List<Map> maps = seasonListMap.get(season);
                if (maps == null || maps.isEmpty()) {
                    data.put("num" + season, "0");
                    data.put("numProportion" + season, "0");
                    data.put("sale" + season, "0");
                    data.put("saleProportion" + season, "0");
                    continue;
                }
                // 数量
                double styleNum = maps.stream().map(e -> e.get(businessCode)).filter(Objects::nonNull).count();

                // 数量
                num += (int) styleNum;
                data.put("num" + season, String.valueOf(styleNum));
                // 数量占比
                double totalStyle = seasonQtyMap.get(season).doubleValue();
                if (totalStyle != 0.0) {
                    BigDecimal result = BigDecimal.valueOf(100 * (styleNum / totalStyle)).setScale(2, RoundingMode.HALF_UP);
                    proportion = proportion.add(result);
                    data.put("numProportion" + season, result.toString() + "%");
                } else {
                    data.put("numProportion" + season, "0");
                }
                // 销量
                double sum = maps.stream().map(map -> (Integer) map.get("qty")).mapToDouble(Integer::doubleValue).sum();
                data.put("sale" + season, String.valueOf(sum));
                saleNum += (int) sum;
                // 销量占比
                double totalSale = seasonTotalMap.get(season).doubleValue();
                if (totalSale != 0.0) {
                    BigDecimal saleResult = BigDecimal.valueOf(100 * (sum / totalSale)).setScale(2, RoundingMode.HALF_UP);
                    data.put("saleProportion" + season, saleResult.toString() + "%");
                    saleProportion = saleProportion.add(saleResult);
                } else {
                    data.put("saleProportion" + season, "0%");
                }
            }
            // 合计
            data.put("numTotal", String.valueOf(num));
            // 总数量占比
            if (totalNum != 0) {

                data.put("numProportionTotal", BigDecimal.valueOf(100 * (num / totalNum)).setScale(2, RoundingMode.HALF_UP) + "%");
            } else {
                data.put("numProportionTotal", "0");
            }
            // 总销量占比
            data.put("saleTotal", String.valueOf(saleNum));
            if (totalQty != 0) {

                data.put("saleProportionTotal", BigDecimal.valueOf(100 * (saleNum / totalQty)).setScale(2, RoundingMode.HALF_UP) + "%");
            } else {
                data.put("saleProportionTotal", "0%");
            }
            datas.add(data);
        }

        // 计算并统计表格数据
        tableData.setCurrent(Long.valueOf(pageNum)).setSize(Long.valueOf(pageSize)).setTotal((long) categoryQtyMap.size());
        tableData.setDatas(datas);
        return Response.of(tableData);
    }

    public List<WarehouseQueryVO> selectWarehouseByDeptIdAndOrgId(Long deptId, Long orgId) {
        List<WarehouseQueryVO> list = new ArrayList<>();
        WarehousePageByOrgIdQry warehouseQry = new WarehousePageByOrgIdQry();
        warehouseQry.setOrgId(orgId).setName("面料仓");
        Response<CustomizePage<WarehouseQueryVO>> warehouseResponse = warehouseRemoteService.selectPageByOrgId(warehouseQry);
        if (!warehouseResponse.isSuccess() || warehouseResponse.getData() == null) {
            return list;
        }
        list = warehouseResponse.getData().getRecords().stream().filter(e -> e.getDeptId().equals(deptId)).collect(Collectors.toList());
        return list;
    }

    /**
     * 商品面料--利用效率
     **/
    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<MaterialTableDataVO> utilizationEfficiency(ProductMaterialQry productMaterialQry) {
        MaterialTableDataVO tableData = new MaterialTableDataVO();
        List<ReportTitleVO> titleList = new ArrayList<>();
        Long orgId = UserHolder.getOrgId();
        // 设置查询参数
        Integer pageNum = productMaterialQry.getPageNum();
        Integer pageSize = productMaterialQry.getPageSize();
        String year = productMaterialQry.getYear();
        titleList.add(new ReportTitleVO().setName("面料分类").setCode("materialCategory"));
        titleList.add(new ReportTitleVO().setName("入仓量").setCode("warehousingQty"));
        titleList.add(new ReportTitleVO().setName("出仓量").setCode("deliveryQty"));
        titleList.add(new ReportTitleVO().setName("库存量").setCode("inventory"));
        titleList.add(new ReportTitleVO().setName("库存金额").setCode("amount"));
        titleList.add(new ReportTitleVO().setName("存量占比").setCode("stockProportion"));
        titleList.add(new ReportTitleVO().setName("金额占比").setCode("amountProportion"));
        titleList.add(new ReportTitleVO().setName("利用率").setCode("useRate"));
        titleList.add(new ReportTitleVO().setName("详情").setCode("detail"));

        String code = "categoryCode";
        List<String> materialCategoryCodes = productMaterialQry.getMaterialCategoryCodes();
        String selectMaterialListSql = FileResourceLoader.getResourceAsString(materialList);
        List<Map> materialList = jdbcService.selectList(selectMaterialListSql, new Object[]{orgId, orgId});
        Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap = materialList.stream()
                .collect(Collectors.toMap(
                        map -> (String) map.get(code),
                        map -> UtilizationEfficiencyBO.builder()
                                .materialCategoryCode((String) map.get("categoryCode"))
                                .materialCategory((String) map.get("categoryName"))
                                .build(),
                        (a, b) -> b
                ));
        String selectWarehouseSql = FileResourceLoader.getResourceAsString("/sql/warehouse.sql");
        List<Map> warehouselList = jdbcService.selectList(selectWarehouseSql, new Object[]{productMaterialQry.getDeptId(), orgId});
        List<String> warehouseList = warehouselList.stream().map(map -> (String) map.get("code")).collect(Collectors.toList());

        // 库存量、库存金额
        String selectInventorySqlBase =
                "select m.category_code AS categoryCode, " +
                        "       m.category_name as categoryName, " +
                        "       mi.material_detail_code AS materialDetailCode, " +
                        "       mi.inventory_qty as inventoryQty, " +
                        "       mi.cost_amount as costAmount, " +
                        "       mi.update_time as updateTime " +
                        "from ods_material m " +
                        "         LEFT JOIN ods_material_inventory mi on m.`code` = mi.material_code " +
                        "where m.type = '面料' " +
                        "  and mi.update_time LIKE CONCAT(?,'%') " +
                        "  AND m.org_id = ? " +
                        "  AND mi.org_id = ?";
        List<Object> argsList = new ArrayList<>();
        argsList.add(year);
        argsList.add(orgId);
        argsList.add(orgId);
        StringBuilder inClause = filterWarehouse(warehouseList, argsList);
        String selectInventorySql = selectInventorySqlBase + inClause.toString() + " " +
                "ORDER BY mi.update_time DESC";
        Object[] args = argsList.toArray(new Object[0]);
        List<Map> inventoryList = jdbcService.selectList(selectInventorySql, args);
        calculateInventory(code, inventoryList, utilizationEfficiencyMap);

        // 入仓量
        String selectPurchaseInSqlBase =
                "select m.category_code as categoryCode, " +
                        "       m.category_name as categoryName, " +
                        "       sum(mi.in_out_qty) as inOutQty " +
                        "from ods_material m " +
                        "         LEFT JOIN ods_material_purchase_in mi on m.`code` = mi.material_code " +
                        "where m.type = '面料' " +
                        "  and mi.update_time LIKE CONCAT(?,'%') " +
                        "  AND m.org_id = ? " +
                        "  AND mi.org_id = ?";
        argsList = new ArrayList<>();
        argsList.add(year);
        argsList.add(orgId);
        argsList.add(orgId);
        inClause = filterWarehouse(warehouseList, argsList);
        String selectPurchaseInSql = selectPurchaseInSqlBase + inClause.toString() + " " +
                "GROUP BY m.category_code, m.category_name";
        args = argsList.toArray(new Object[0]);
        List<Map> purchaseInList = jdbcService.selectList(selectPurchaseInSql, args);
        calculatePurchaseIn(code, purchaseInList, utilizationEfficiencyMap);

        // 出仓量
        String selectIssuingSqlBase =
                "select m.category_code as categoryCode, " +
                        "       m.category_name as categoryName, " +
                        "       sum(mi.issuing_number) as issuingNumber " +
                        "from ods_material m " +
                        "         LEFT JOIN ods_material_issuing mi on m.`code` = mi.code " +
                        "where " +
                        "  mi.update_time LIKE CONCAT(?,'%') " +
                        "  AND m.org_id = ? " +
                        "  AND mi.org_id = ?";
        argsList = new ArrayList<>();
        argsList.add(year);
        argsList.add(orgId);
        argsList.add(orgId);
        inClause = filterWarehouse(warehouseList, argsList);
        String selectIssuingSql = selectIssuingSqlBase + inClause.toString() + " " +
                "GROUP BY m.category_code, m.category_name";
        args = argsList.toArray(new Object[0]);
        List<Map> issuingList = jdbcService.selectList(selectIssuingSql, args);
        calculateIssuing(code, issuingList, utilizationEfficiencyMap);

        calculateUtilizationEfficiency(utilizationEfficiencyMap);

        // 转换
        List<Map<String, String>> data = convertUtilizationEfficiencyTable(materialCategoryCodes, utilizationEfficiencyMap);


        Collections.sort(data, new Comparator<Map<String, String>>() {
            @Override
            public int compare(Map<String, String> o1, Map<String, String> o2) {
                BigDecimal useRate1 = parseUseRate(o1.get("useRate"));
                BigDecimal useRate2 = parseUseRate(o2.get("useRate"));
                int useRateComparison = useRate2.compareTo(useRate1);
                if (useRateComparison != 0) {
                    return useRateComparison;
                } else {
                    BigDecimal inventory1 = parseInventory(o1.get("inventory"));
                    BigDecimal inventory2 = parseInventory(o2.get("inventory"));
                    return inventory2.compareTo(inventory1);
                }
            }
        });


        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, data.size());
        List<Map<String, String>> pagedDatas = data.subList(startIndex, endIndex);
        tableData.setSize((long) pagedDatas.size());
        tableData.setDatas(pagedDatas);
        tableData.setTitle(titleList);
        tableData.setTotal((long) data.size());
        return Response.of(tableData);
    }

    private static BigDecimal parseUseRate(String useRateStr) {
        if (useRateStr != null && !useRateStr.trim().isEmpty()) {
            try {
                return new BigDecimal(useRateStr.trim().replaceAll("%", ""));
            } catch (NumberFormatException e) {
                return BigDecimal.ZERO;
            }
        }
        return BigDecimal.ZERO;
    }

    private static BigDecimal parseInventory(String inventoryStr) {
        if (inventoryStr != null && !inventoryStr.trim().isEmpty()) {
            try {
                return new BigDecimal(inventoryStr.trim());
            } catch (NumberFormatException e) {
                return BigDecimal.ZERO;
            }
        }
        return BigDecimal.ZERO;
    }

    @NotNull
    private static StringBuilder filterWarehouse(List<String> warehouseList, List<Object> argsList) {
        StringBuilder inClause = new StringBuilder();
        if (!warehouseList.isEmpty()) {
            inClause.append("  AND mi.warehouse_code IN (");
            for (int i = 0; i < warehouseList.size(); i++) {
                if (i > 0) {
                    inClause.append(", ");
                }
                inClause.append("?");
                argsList.add(warehouseList.get(i));
            }
            inClause.append(")");
        } else {
            inClause.append("  AND 1=2");
        }
        return inClause;
    }

    private void calculateUtilizationEfficiency(Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap) {
        for (Map.Entry<String, UtilizationEfficiencyBO> voEntry : utilizationEfficiencyMap.entrySet()) {
            UtilizationEfficiencyBO bo = voEntry.getValue();

            BigDecimal warehousingQty = bo.getWarehousingQty();
            BigDecimal deliveryQty = bo.getDeliveryQty();
            BigDecimal initInventory = bo.getInitInventory();

            if (deliveryQty != null && initInventory != null) {
                if (warehousingQty == null) {
                    warehousingQty = BigDecimal.ZERO;
                }
                BigDecimal totalInventory = warehousingQty.add(initInventory);
                if (totalInventory.compareTo(BigDecimal.ZERO) != 0) {
                    BigDecimal useRate = deliveryQty.divide(totalInventory, 6, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                    useRate = useRate.setScale(2, RoundingMode.HALF_UP);
                    bo.setUseRate(useRate.toString() + "%");
                } else {
                    bo.setUseRate("0%");
                }
            } else {
                bo.setUseRate("0%");
            }
        }
    }

    private void calculateIssuing(String code, List<Map> issuingList, Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap) {
        for (Map issuingMap : issuingList) {
            String categoryCode = (String) issuingMap.get(code);
            BigDecimal issuingNumber = TypeConversionUtil.toBigDecimal(issuingMap.get("issuingNumber")).setScale(2, RoundingMode.HALF_UP);

            if (!utilizationEfficiencyMap.containsKey(categoryCode)) {
                continue;
            }
            UtilizationEfficiencyBO utilizationEfficiencyBO = utilizationEfficiencyMap.get(categoryCode);
            utilizationEfficiencyBO.setDeliveryQty(issuingNumber);
            utilizationEfficiencyMap.put(categoryCode, utilizationEfficiencyBO);
        }
    }

    /**
     * 商品面料--利用效率明细
     **/
    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<MaterialTableDataVO> utilizationEfficiencyDetail(ProductMaterialQry productMaterialQry) {
        MaterialTableDataVO tableData = new MaterialTableDataVO();
        List<Map<String, String>> datas = new ArrayList<>();
        List<ReportTitleVO> titleList = new ArrayList<>();
        Long orgId = UserHolder.getOrgId();
        // 设置查询参数
        Integer pageNum = productMaterialQry.getPageNum();
        Integer pageSize = productMaterialQry.getPageSize();
        String year = productMaterialQry.getYear();
        titleList.add(new ReportTitleVO().setName("面料编码").setCode("materialCode"));
        titleList.add(new ReportTitleVO().setName("成分").setCode("ingredient"));
        titleList.add(new ReportTitleVO().setName("幅宽(cm)").setCode("breadth"));
        titleList.add(new ReportTitleVO().setName("克重(g)").setCode("gramWeight"));
        titleList.add(new ReportTitleVO().setName("损耗率").setCode("wasteRate"));
        titleList.add(new ReportTitleVO().setName("入仓量").setCode("warehousingQty"));
        titleList.add(new ReportTitleVO().setName("出仓量").setCode("deliveryQty"));
        titleList.add(new ReportTitleVO().setName("库存量").setCode("inventory"));
        titleList.add(new ReportTitleVO().setName("库存金额").setCode("amount"));
        titleList.add(new ReportTitleVO().setName("存量占比").setCode("stockProportion"));
        titleList.add(new ReportTitleVO().setName("金额占比").setCode("amountProportion"));
        titleList.add(new ReportTitleVO().setName("利用率").setCode("useRate"));

        String code = "code";
        List<String> materialCategoryCodes = productMaterialQry.getMaterialCategoryCodes();
        String selectMaterialListSql = "SELECT DISTINCT CODE,ingredient,breadth,gram_weight,waste_rate FROM ods_material WHERE org_id = ? ";
        StringBuilder sb = new StringBuilder();
        if (ObjectUtils.isNotEmpty(materialCategoryCodes)) {
            sb.append("and category_code IN (");
            for (String materialCategoryCode : materialCategoryCodes) {
                sb.append("'").append(materialCategoryCode).append("'").append(",");
            }
            sb.delete(sb.length() - 1, sb.length());
            sb.append(")");
        }
        selectMaterialListSql += sb.toString();
        List<Map> materialList = jdbcService.selectList(selectMaterialListSql, new Object[]{orgId});
        Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap = materialList.stream()
                .collect(Collectors.toMap(
                        map -> (String) map.get(code),
                        map -> UtilizationEfficiencyBO.builder()
                                .materialCode((String) map.get("code"))
                                .ingredient((String) map.get("ingredient"))
                                .breadth((BigDecimal) map.get("breadth"))
                                .gramWeight((BigDecimal) map.get("gramWeight"))
                                .wasteRate((BigDecimal) map.get("wasteRate"))
                                .build(),
                        (a, b) -> b
                ));
        String selectWarehouseSql = FileResourceLoader.getResourceAsString("/sql/warehouse.sql");
        List<Map> warehouselList = jdbcService.selectList(selectWarehouseSql, new Object[]{productMaterialQry.getDeptId(), orgId});
        List<String> warehouseList = warehouselList.stream().map(map -> (String) map.get("code")).collect(Collectors.toList());

        // 库存量、库存金额
        String selectInventorySqlBase =
                "select m.code AS code, " +
                        "       mi.material_detail_code AS materialDetailCode, " +
                        "       mi.inventory_qty as inventoryQty, " +
                        "       mi.cost_amount as costAmount, " +
                        "       mi.update_time as updateTime " +
                        "from ods_material m " +
                        "         LEFT JOIN ods_material_inventory mi on m.`code` = mi.material_code " +
                        "where m.type = '面料' " +
                        "  and mi.update_time LIKE CONCAT(?,'%') " +
                        "  AND m.org_id = ? " +
                        "  AND mi.org_id = ?";
        List<Object> argsList = new ArrayList<>();
        argsList.add(year);
        argsList.add(orgId);
        argsList.add(orgId);
        StringBuilder inClause = filterWarehouse(warehouseList, argsList);
        String selectInventorySql = selectInventorySqlBase + inClause.toString() + " " +
                "ORDER BY mi.update_time DESC";
        Object[] args = argsList.toArray(new Object[0]);
        List<Map> inventoryList = jdbcService.selectList(selectInventorySql, args);
        calculateInventory(code, inventoryList, utilizationEfficiencyMap);

        // 入仓量
        String selectPurchaseInSqlBase =
                "select m.code , " +
                        "       sum(mi.in_out_qty) as inOutQty " +
                        "from ods_material m " +
                        "         LEFT JOIN ods_material_purchase_in mi on m.`code` = mi.material_code " +
                        "where m.type = '面料' " +
                        "  and mi.update_time LIKE CONCAT(?,'%') " +
                        "  AND m.org_id = ? " +
                        "  AND mi.org_id = ?";
        argsList = new ArrayList<>();
        argsList.add(year);
        argsList.add(orgId);
        argsList.add(orgId);
        inClause = filterWarehouse(warehouseList, argsList);
        String selectPurchaseInSql = selectPurchaseInSqlBase + inClause.toString() + " " +
                "GROUP BY m.code";
        args = argsList.toArray(new Object[0]);
        List<Map> purchaseInList = jdbcService.selectList(selectPurchaseInSql, args);
        calculatePurchaseIn(code, purchaseInList, utilizationEfficiencyMap);

        // 出仓量
        String selectIssuingSqlBase =
                "select m.code, " +
                        "       sum(mi.issuing_number) as issuingNumber " +
                        "from ods_material m " +
                        "         LEFT JOIN ods_material_issuing mi on m.`code` = mi.code " +
                        "where " +
                        "  mi.update_time LIKE CONCAT(?,'%') " +
                        "  AND m.org_id = ? " +
                        "  AND mi.org_id = ?";
        argsList = new ArrayList<>();
        argsList.add(year);
        argsList.add(orgId);
        argsList.add(orgId);
        inClause = filterWarehouse(warehouseList, argsList);
        String selectIssuingSql = selectIssuingSqlBase + inClause.toString() + " " +
                "GROUP BY m.code";
        args = argsList.toArray(new Object[0]);
        List<Map> issuingList = jdbcService.selectList(selectIssuingSql, args);
        calculateIssuing(code, issuingList, utilizationEfficiencyMap);

        calculateUtilizationEfficiency(utilizationEfficiencyMap);

        // 转换
//        List<Map<String, String>> data = convertUtilizationEfficiencyTable(materialCategoryCodes, utilizationEfficiencyMap);
        List<Map<String, String>> data = new ArrayList<>();
        for (Map.Entry<String, UtilizationEfficiencyBO> voEntry : utilizationEfficiencyMap.entrySet()) {
            /*if (!CollectionUtils.isEmpty(materialCategoryCodes) && !materialCategoryCodes.contains(voEntry.getKey())) {
                continue;
            }*/
            UtilizationEfficiencyBO vo = voEntry.getValue();
            /*if (vo.getInventory() == null && vo.getWarehousingQty() == null && vo.getDeliveryQty() == null && vo.getAmount() == null) {
                continue;
            }*/

            Map<String, String> dataMap = new HashMap<>();
            for (Field field : UtilizationEfficiencyBO.class.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    Object value = field.get(vo);
                    if (value != null) {
                        dataMap.put(field.getName(), value.toString());
                    } else {
                        dataMap.put(field.getName(), "");
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            data.add(dataMap);
        }
        // 根据利用率、库存量排序
        // 排序
        data.sort(Comparator.comparing((Map<String, String> m) ->
                        -Double.parseDouble(m.get("useRate").replace("%", "")))
                .thenComparing(m -> {
                    String inventoryStr = m.get("inventory");
                    return inventoryStr == null || inventoryStr.isEmpty() ? 0 : -Integer.parseInt(inventoryStr);
                }));

        int startIndex = (pageNum - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, data.size());
        List<Map<String, String>> pagedDatas = data.subList(startIndex, endIndex);
        tableData.setSize((long) pagedDatas.size());
        tableData.setDatas(pagedDatas);
        tableData.setTitle(titleList);
        tableData.setTotal((long) data.size());
        return Response.of(tableData);
    }

    private static List<Map<String, String>> convertUtilizationEfficiencyTable(List<String> materialCategoryCodes, Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap) {
        List<Map<String, String>> datas = new ArrayList<>();
        for (Map.Entry<String, UtilizationEfficiencyBO> voEntry : utilizationEfficiencyMap.entrySet()) {
            if (!CollectionUtils.isEmpty(materialCategoryCodes) && !materialCategoryCodes.contains(voEntry.getKey())) {
                continue;
            }
            UtilizationEfficiencyBO vo = voEntry.getValue();
//            if (vo.getInventory() == null && vo.getWarehousingQty() == null && vo.getDeliveryQty() == null && vo.getAmount() == null) {
//                continue;
//            }

            Map<String, String> dataMap = new HashMap<>();
            for (Field field : UtilizationEfficiencyBO.class.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    Object value = field.get(vo);
                    if (value != null) {
                        dataMap.put(field.getName(), value.toString());
                    } else {
                        dataMap.put(field.getName(), "");
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            datas.add(dataMap);
        }
        return datas;
    }

    private void calculatePurchaseIn(String code, List<Map> purchaseInList, Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap) {
        for (Map purchaseMap : purchaseInList) {
            String categoryCode = (String) purchaseMap.get(code);
            BigDecimal purchaseQty = TypeConversionUtil.toBigDecimal(purchaseMap.get("inOutQty"));

            if (!utilizationEfficiencyMap.containsKey(categoryCode)) {
                continue;
            }
            UtilizationEfficiencyBO utilizationEfficiencyBO = utilizationEfficiencyMap.get(categoryCode);
            utilizationEfficiencyBO.setWarehousingQty(purchaseQty);
            utilizationEfficiencyMap.put(categoryCode, utilizationEfficiencyBO);
        }
    }

    private static void calculateInventory(String code, List<Map> inventoryList, Map<String, UtilizationEfficiencyBO> utilizationEfficiencyMap) {
        Map<String, Map> inventoryDetailMap = new HashMap<>();
        Map<String, Map> initInventoryDetailMap = new HashMap<>();

        BigDecimal inventoryTotal = BigDecimal.ZERO;
        BigDecimal amountTotal = BigDecimal.ZERO;

        String inventoryQtyStr = "inventoryQty";
        String costAmountStr = "costAmount";

        // 获取最新库存
        for (Map inventoryMap : inventoryList) {
            String materialDeatilCode = (String) inventoryMap.get("materialDetailCode");
            initInventoryDetailMap.put(materialDeatilCode, inventoryMap);
            if (inventoryDetailMap.containsKey(materialDeatilCode)) {
                continue;
            }
            inventoryDetailMap.put(materialDeatilCode, inventoryMap);
        }


        for (Map.Entry<String, Map> entry : inventoryDetailMap.entrySet()) {
            Map item = entry.getValue();
            String categoryCode = (String) item.get(code);
            if (!utilizationEfficiencyMap.containsKey(categoryCode)) {
                continue;
            }
            BigDecimal amount = TypeConversionUtil.toBigDecimal(item.get(costAmountStr));
            BigDecimal inventoryQty = TypeConversionUtil.toBigDecimal(item.get(inventoryQtyStr));
            if (amount != null) {
                amountTotal = amountTotal.add(amount);
            }
            if (inventoryQty != null) {
                inventoryTotal = inventoryTotal.add(inventoryQty);
            }

            UtilizationEfficiencyBO utilizationEfficiencyBO = utilizationEfficiencyMap.get(categoryCode);
            utilizationEfficiencyBO.setInventory(
                    inventoryQty != null ?
                            inventoryQty.add(utilizationEfficiencyBO.getInventory() != null ? utilizationEfficiencyBO.getInventory() : BigDecimal.ZERO) :
                            utilizationEfficiencyBO.getInventory()
            );
            utilizationEfficiencyBO.setAmount(
                    amount != null ?
                            amount.add(utilizationEfficiencyBO.getAmount() != null ? utilizationEfficiencyBO.getAmount() : BigDecimal.ZERO) :
                            utilizationEfficiencyBO.getAmount()
            );
            utilizationEfficiencyMap.put(categoryCode, utilizationEfficiencyBO);
        }

        // 期初库存
        for (Map.Entry<String, Map> entry : initInventoryDetailMap.entrySet()) {
            Map item = entry.getValue();
            String categoryCode = (String) item.get(code);
            if (!utilizationEfficiencyMap.containsKey(categoryCode)) {
                continue;
            }
            BigDecimal inventoryQty = TypeConversionUtil.toBigDecimal(item.get(inventoryQtyStr));
            if (inventoryQty == null) {
                continue;
            }
            UtilizationEfficiencyBO utilizationEfficiencyBO = utilizationEfficiencyMap.get(categoryCode);
            BigDecimal initInventory = utilizationEfficiencyBO.getInitInventory() != null ?
                    utilizationEfficiencyBO.getInitInventory() :
                    BigDecimal.ZERO;
            utilizationEfficiencyBO.setInitInventory(inventoryQty.add(initInventory));
            utilizationEfficiencyMap.put(categoryCode, utilizationEfficiencyBO);
        }

        BigDecimal finalAmountTotal = amountTotal;
        BigDecimal finalInventoryTotal = inventoryTotal;

        for (Map.Entry<String, UtilizationEfficiencyBO> entry : utilizationEfficiencyMap.entrySet()) {
            UtilizationEfficiencyBO v = entry.getValue();

            BigDecimal inventory = v.getInventory() != null ? v.getInventory() : BigDecimal.ZERO;
            BigDecimal amount = v.getAmount() == null ? BigDecimal.ZERO : v.getAmount();

            BigDecimal stockProportion = inventory.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO :
                    inventory.divide(finalInventoryTotal, 10, RoundingMode.HALF_UP);
            BigDecimal amountProportion = finalAmountTotal.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO :
                    amount.divide(finalAmountTotal, 10, RoundingMode.HALF_UP);

            v.setStockProportion(String.format("%.2f", stockProportion.multiply(BigDecimal.valueOf(100))) + "%");
            v.setAmountProportion(String.format("%.2f", amountProportion.multiply(BigDecimal.valueOf(100))) + "%");
        }
    }


    /**
     * 商品面料--利用效率明细图表
     **/
    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<UtilizationEfficiencyChartVO> utilizationEfficiencyChart(ProductMaterialQry productMaterialQry) {
        UtilizationEfficiencyChartVO vo = new UtilizationEfficiencyChartVO();
        ChartVO chart = new ChartVO();
        List<String> items = chart.getItems();
        items.add("利用率");
        List<List<Object>> datas = new ArrayList<>();
        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(PageQuery.MAX_PAGE_SIZE);
        Response<MaterialTableDataVO> detailResponse = this.utilizationEfficiencyDetail(productMaterialQry);
        if (detailResponse.isSuccess() && detailResponse.get().getDatas() != null) {
            for (Map map : detailResponse.get().getDatas()) {
                List<Object> data = new ArrayList<>();
                data.add(map.get("materialCode"));
                data.add(map.get("useRate") == null || map.get("useRate").equals("") ? BigDecimal.ZERO : new BigDecimal(map.get("useRate").toString().replace("%", "")));
                datas.add(data);
            }
        }
        // 整体利用率
        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(1);
        Response<MaterialTableDataVO> MaterialTableDataVOResponse = this.utilizationEfficiency(productMaterialQry);
        if (MaterialTableDataVOResponse.isSuccess() && ObjectUtils.isNotEmpty(MaterialTableDataVOResponse.get().getDatas())) {
            Map map = MaterialTableDataVOResponse.get().getDatas().get(0);
            vo.setTotal(map.get("useRate") == null || map.get("useRate").equals("") ? "0%" : map.get("useRate").toString());
        }
        chart.setItems(items);
        chart.setDatas(datas);
        vo.setChart(chart);
        return Response.of(vo);
    }


    /**
     * 方法：设置商品面料子标题
     **/
    public List<ReportTitleVO> setChildMaterialApplyTitle(String productName, int size, String dictValue) {
        List<ReportTitleVO> titleList = new ArrayList<>();
        if (productName != null && size > 2) {
            ReportTitleVO title = new ReportTitleVO();
            title.setName(productName + "数").setCode("num" + dictValue);
            ReportTitleVO title2 = new ReportTitleVO();
            title2.setName("占比").setCode("numProportion" + dictValue);
            titleList.add(title);
            titleList.add(title2);
        }
        // 设置表头
        ReportTitleVO title = new ReportTitleVO();
        title.setName("销量").setCode("sale" + dictValue);
        ReportTitleVO title2 = new ReportTitleVO();
        title2.setName("占比").setCode("saleProportion" + dictValue);
        titleList.add(title);
        titleList.add(title2);
        return titleList;
    }

    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<MaterialTableDataVO> utilizationEfficiencyExport(HttpServletResponse response, ProductMaterialQry productMaterialQry) {
        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(PageQuery.MAX_PAGE_SIZE);

        Response<MaterialTableDataVO> MaterialTableDataVOResponse = this.utilizationEfficiency(productMaterialQry);
        if (!MaterialTableDataVOResponse.isSuccess()) {
            return Response.failure();
        }

        List tableData = MaterialTableDataVOResponse.getData().getDatas();
        List<Column> columns = new ArrayList<>();

        for (ReportTitleVO t : MaterialTableDataVOResponse.getData().getTitle()) {
            columns.add(new Column().setTitle(t.getName()).setFieldName(t.getCode()));
        }

        try {
            excelDataExporter.export(response, columns, tableData, productMaterialQry.getYear() + "年整体面料利用效率");
        } catch (Exception e) {
            log.error("导出异常==>", e.getMessage(), e);
            return Response.failure();
        }
        return Response.success();


    }

    public Response<MaterialTableDataVO> materialApplyExport(HttpServletResponse response, ProductMaterialQry productMaterialQry) {
        Response<MaterialTableDataVO> MaterialTableDataVOResponse = this.materialApply(productMaterialQry);
        if (!MaterialTableDataVOResponse.isSuccess()) {
            return Response.failure("暂无可导出数据");
        }

        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(PageQuery.MAX_PAGE_SIZE);

        List tableData = MaterialTableDataVOResponse.getData().getDatas();
        List<Column> columns = new ArrayList<>();
        List<ReportTitleVO> titles = MaterialTableDataVOResponse.getData().getTitle();
        List<ReportTitleVO> childTitles = getChildTitleWithoutFirst(titles);
        for (ReportTitleVO t : childTitles) {
            columns.add(new Column().setTitle(t.getName()).setFieldName(t.getCode()));
        }
        List<DictVO> dicts = dictionaryProvider.selectList(DictionaryConstants.PRODUCT_STRUCT_TYPE);
        DictVO dict = dicts.stream().filter(e -> e.getValue().equals(productMaterialQry.getProductDimension())).findFirst().orElse(null);
        try {
            excelDataExporter.export(response, columns, tableData, dict.getLabel() + "面料分布数据");
        } catch (Exception e) {
            log.error("导出异常==>", e.getMessage(), e);
            return Response.failure();
        }
        return Response.success();
    }

    public Response<MaterialTableDataVO> styleDetailExport(HttpServletResponse response, ProductMaterialQry productMaterialQry) {
        Response<MaterialTableDataVO> MaterialTableDataVOResponse = this.styleDetail(productMaterialQry);
        if (!MaterialTableDataVOResponse.isSuccess()) {
            return Response.failure("暂无可导出数据");
        }

        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(PageQuery.MAX_PAGE_SIZE);

        List tableData = MaterialTableDataVOResponse.getData().getDatas();
        List<Column> columns = new ArrayList<>();
        List<ReportTitleVO> titles = MaterialTableDataVOResponse.getData().getTitle();
        List<ReportTitleVO> childTitles = getChildTitleWithoutFirst(titles);
        for (ReportTitleVO t : childTitles) {
            columns.add(new Column().setTitle(t.getName()).setFieldName(t.getCode()));
        }
        List<DictVO> dicts = dictionaryProvider.selectList(DictionaryConstants.PRODUCT_STRUCT_TYPE);
        DictVO dict = dicts.stream().filter(e -> e.getValue().equals(productMaterialQry.getProductDimension())).findFirst().orElse(null);
        try {
            excelDataExporter.export(response, columns, tableData, productMaterialQry.getMaterialCategoryName() + dict.getLabel() + "明细");
        } catch (Exception e) {
            log.error("导出异常==>", e.getMessage(), e);
            return Response.failure();
        }
        return Response.success();
    }

    public Response<MaterialTableDataVO> materialDetailExport(HttpServletResponse response, ProductMaterialQry productMaterialQry) {
        Response<MaterialTableDataVO> MaterialTableDataVOResponse = this.materialDetail(productMaterialQry);
        if (!MaterialTableDataVOResponse.isSuccess()) {
            return Response.failure("暂无可导出数据");
        }

        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(PageQuery.MAX_PAGE_SIZE);

        List tableData = MaterialTableDataVOResponse.getData().getDatas();
        List<Column> columns = new ArrayList<>();
        List<ReportTitleVO> titles = MaterialTableDataVOResponse.getData().getTitle();
        List<ReportTitleVO> childTitles = getChildTitleWithoutFirst(titles);
        for (ReportTitleVO t : childTitles) {
            columns.add(new Column().setTitle(t.getName()).setFieldName(t.getCode()));
        }
        try {
            excelDataExporter.export(response, columns, tableData, productMaterialQry.getMaterialCategoryName() + "面料详情");
        } catch (Exception e) {
            log.error("导出异常==>", e.getMessage(), e);
            return Response.failure();
        }
        return Response.success();
    }

    @DataSourceSwitch(DataSourceEnum.ODS)
    public Response<MaterialTableDataVO> utilizationEfficiencyDetailExport(HttpServletResponse response, ProductMaterialQry productMaterialQry) {
        productMaterialQry.setPageNum(1);
        productMaterialQry.setPageSize(PageQuery.MAX_PAGE_SIZE);

        Response<MaterialTableDataVO> MaterialTableDataVOResponse = this.utilizationEfficiencyDetail(productMaterialQry);
        if (!MaterialTableDataVOResponse.isSuccess()) {
            return Response.failure("暂无可导出数据");
        }

        List tableData = MaterialTableDataVOResponse.getData().getDatas();
        List<Column> columns = new ArrayList<>();

        for (ReportTitleVO t : MaterialTableDataVOResponse.getData().getTitle()) {
            columns.add(new Column().setTitle(t.getName()).setFieldName(t.getCode()));
        }

        try {
            excelDataExporter.export(response, columns, tableData, productMaterialQry.getYear() + "年" + productMaterialQry.getMaterialCategoryName() + "利用效率");
        } catch (Exception e) {
            log.error("导出异常==>", e.getMessage(), e);
            return Response.failure();
        }
        return Response.success();
    }

    public List<ReportTitleVO> getChildTitleWithoutFirst(List<ReportTitleVO> titles) {
        List<ReportTitleVO> childTitles = new ArrayList<>();
        for (int i = 0; i < titles.size(); i++) {
            ReportTitleVO title = titles.get(i);
            if (title.getChildren() != null) {
                for (ReportTitleVO child : title.getChildren()) {
                    if (i == 0) {
                        // 第一个 ReportTitleVO 不拼接
                        childTitles.add(child);
                    } else {
                        child.setName(title.getName() + child.getName());
                        childTitles.add(child);
                    }
                }
            }
        }
        return childTitles;
    }
}
